package dissertation.services.project;

import com.intellij.openapi.compiler.CompileScope;
import com.intellij.openapi.compiler.CompileStatusNotification;
import com.intellij.openapi.compiler.CompilerManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModulePointer;
import com.intellij.openapi.module.ModulePointerManager;
import com.intellij.openapi.project.Project;
import com.intellij.packaging.artifacts.*;
import com.intellij.packaging.impl.compiler.ArtifactCompileScope;
import com.intellij.packaging.impl.compiler.ArtifactsWorkspaceSettings;
import com.intellij.packaging.impl.elements.ArchivePackagingElement;
import com.intellij.packaging.impl.elements.ArtifactPackagingElement;
import com.intellij.packaging.impl.elements.DirectoryCopyPackagingElement;
import com.intellij.packaging.impl.elements.ProductionModuleOutputPackagingElement;
import dissertation.common.utils.applicationxml.factory.ApplicationXMLBuilderFactory;
import dissertation.services.ejbmodule.EJBModuleService;
import dissertation.services.ejbmodule.IEJBModuleService;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static dissertation.common.constants.Constants.*;

public class ProjectArtifactService implements IProjectArtifactService {
    private static final Logger LOGGER = Logger.getInstance(ProjectArtifactService.class);
    private IEJBModuleService ejbModuleService;

    public ProjectArtifactService() {
        ejbModuleService = new EJBModuleService();
    }

    @Override
    public ArchivePackagingElement createEARArchivePackagingElement(Project project) {
        ArtifactManager artifactManager = ArtifactManager.getInstance(project);
        ArchivePackagingElement earArchivePackagingElement = new ArchivePackagingElement(project.getName() + EAR_EXTENSION);

        Artifact[] artifacts = artifactManager.getArtifacts();
        List<String> jarNames = getJarNames(artifacts);

        Module[] modules = ModuleManager.getInstance(project).getModules();
        if (modules.length > 0) {
            String ejbVersion = ejbModuleService.getEJBVersion(modules[0]);
            addAllArtifacts(project, earArchivePackagingElement, artifacts);
            addApplicationDescriptor(earArchivePackagingElement, jarNames, ejbVersion);
        }

        return earArchivePackagingElement;
    }

    @SuppressWarnings("ResultOfMethodCallIgnored")
    private void addApplicationDescriptor(ArchivePackagingElement earArchivePackagingElement, List<String> jarNames, String ejbVersion) {
        PrintWriter pw = null;
        File resourcesDirectory = null;
        try {
            resourcesDirectory = new File(TEMP_DIR, "tmp" + System.currentTimeMillis());
            resourcesDirectory.mkdir();
            File projectMetaInfDirectory = new File(resourcesDirectory, META_INF);
            projectMetaInfDirectory.mkdir();
            pw = new PrintWriter(new File(projectMetaInfDirectory, APPLICATION_XML));
            pw.print(ApplicationXMLBuilderFactory.getInstance().getBuilder(ejbVersion).buildApplicationXML(jarNames));
        } catch (IOException e1) {
            LOGGER.info("Exception occurred while creating application.xml file. Details: " + e1.getMessage());
        } finally {
            if (pw != null) {
                pw.close();
            }
        }

        earArchivePackagingElement.addFirstChild(new DirectoryCopyPackagingElement(resourcesDirectory.getAbsolutePath()));
    }

    private void addAllArtifacts(Project project, ArchivePackagingElement earArchivePackagingElement, Artifact[] artifacts) {
        for (Artifact artifact : artifacts) {
            ArtifactPointerManager artifactPointerManager = ArtifactPointerManager.getInstance(project);
            ArtifactPointer artifactPointer = artifactPointerManager.createPointer(artifact);
            ArtifactPackagingElement artifactPackagingElement = new ArtifactPackagingElement(project, artifactPointer);
            earArchivePackagingElement.addFirstChild(artifactPackagingElement);
        }
    }

    private List<String> getJarNames(Artifact[] artifacts) {
        List<String> jarNames = new ArrayList<String>();
        for (Artifact artifact : artifacts) {
            jarNames.add(artifact.getRootElement().getName());
        }
        return jarNames;
    }

    @Override
    public ArchivePackagingElement createEARModuleArchivePackagingElement(Module module) {
        ModulePointer modulePointer = ModulePointerManager.getInstance(module.getProject()).create(module);
        ProductionModuleOutputPackagingElement productionModuleOutputPackagingElement = new ProductionModuleOutputPackagingElement(module.getProject(), modulePointer);
        ArchivePackagingElement archivePackagingElement = new ArchivePackagingElement(module.getName() + JAR_EXTENSION);
        archivePackagingElement.addFirstChild(productionModuleOutputPackagingElement);

        File moduleDirectory = new File(module.getModuleFilePath()).getParentFile();
        File metaInfDirectory = new File(moduleDirectory, RESOURCES_DIRECTORY_NAME);
        if (metaInfDirectory.exists()) {
            DirectoryCopyPackagingElement directoryCopyPackagingElement =
                    new DirectoryCopyPackagingElement(metaInfDirectory.getAbsolutePath());
            archivePackagingElement.addFirstChild(directoryCopyPackagingElement);
        }
        return archivePackagingElement;
    }

    @Override
    public void removeArtifacts(Project project) {
        ArtifactManager artifactManager = ArtifactManager.getInstance(project);
        ModifiableArtifactModel modifiableModel = artifactManager.createModifiableModel();
        Artifact[] artifacts = modifiableModel.getArtifacts();
        for (Artifact artifact : artifacts) {
            modifiableModel.removeArtifact(artifact);
            modifiableModel.commit();
        }
    }

    @Override
    public void buildArtifact(CompileStatusNotification compileStatusNotification, Project project, Artifact artifact) {
        final CompileScope scope = ArtifactCompileScope.createArtifactsScope(project, Collections.singletonList(artifact));
        ArtifactsWorkspaceSettings.getInstance(project).setArtifactsToBuild(Collections.singletonList(artifact));
        CompilerManager.getInstance(project).compile(scope, compileStatusNotification);
    }
}
